newtypeとsmart constructorのmodule
以下の組み合わせのmoduleを設計する
newtype
型は公開
値コンストラクタは非公開
smart constructor
公開
unwrapする関数
公開
例 ref
code:Password.hs
module Password
( Password -- ←これは型
, unPassword
, mkPassword
) where
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
newtype Password = Password ByteString
unPassword :: Password -> ByteString
unPassword (Password password) = password
mkPassword :: ByteString -> Maybe Password
mkPassword pwd
| ByteString.null pwd = Nothing
| otherwise = Just (Password pwd)
Password の値constructorは公開せずに、mkPasswordのみを公開している
Password型を生成するために、mkPasswordを使うように強制できる
mkPasswordでは、validationを行い、不正なデータでPasswordを定義させない
ここでは空文字のPasswordを指定させないようにしている
Passwordの値consturctorを公開していないので、値を取り出すためにunPassword関数が必要
https://thomashoneyman.com/guides/real-world-halogen/design-data-pure-functions/#:~:text=Restricting%20The%20Domain%20Using%20Smart%20Constructors
2つのアプローチ ref
「Usernameは空文字ではいけない」という仕様があって、それをどう表現するか
code:A.hs
newtype Username = Username String
mkUsername :: String -> Maybe Username
mkUsername "" = Nothing
mkUsername s = Just (Username s)
code:B.hs
newtype Username = Username NonEmptyString
Aのほうは、Stringでnewtypeして、空文字validationするために、smart constructorを使っている
Bの方は、そもそも型レベルで空文字を受け付けない
今後の仕様の変わりようも考えると、Aの方が柔軟な気もする
validationに「記号から始まってはいけない」とか「3文字以上」とかの要件が加わる可能性はある
Cとして、NonEmptySringにsmart consturctorを使う、というのもあり得る
これが良いと思うmrsekut.icon
mkする場所やunwrapする場所は、外部との境界値になる
ref プログラムの外部との境界で、仕様を満たしたデータ型に変換する
unwrapさせない工夫
F# for Fun and Profit ref
unwrapするのではなく、unwrapしたものに適用する関数を渡すようにする
こうではなく
code:before.fs
address |> EmailAddress.value |> printfn "the value is %s"
こうする
code:after.fs
address |> EmailAddress.apply (printfn "the value is %s")
こうすることで、unwrapされた値を持ち回ることを防ぐことができる
unwrapした値を取り出せないので
でもidを適用すれば取り出せるかmrsekut.icon
PatternSynonyms
ここに出てきた
https://logicoffee.hatenablog.com/entry/2018/12/14/212521
Haskell mini-patterns handbook :: Kowainik
簡潔でわかりやすい
/mrsekut-book-97816805025/119 (The Integrity of Simple Values)~
ここでは、返り値をEither ソレ Stringにしようというふうに書かれている
https://logicoffee.hatenablog.com/entry/2018/12/14/212521
https://wiki.haskell.org/Smart_constructors
https://kazu-yamamoto.hatenablog.jp/entry/20080828/1219888005
smart constructorの話ではないけど
Designing with types ref
String50とString100などを個別に作りつつも互換性をもたせる方法
#functional_mini_patterns